package com.example.novelweb.utils.WordCheck;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class DfaTree {
  private final Node root = new Node();
  private Filter<Character> charFilter = StopCharacter::isNotStopChar;

  public DfaTree() {
  }

  public DfaTree setCharFilter(Filter<Character> charFilter) {
    this.charFilter = charFilter;
    return this;
  }

  public DfaTree addWords(Collection<String> sensitiveWords) {
    for (String word : sensitiveWords) {
      this.addWord(word);
    }
    return this;
  }

  public DfaTree addWords(String... sensitiveWords) {
    for (String word : sensitiveWords) {
      this.addWord(word);
    }
    return this;
  }

  public DfaTree addWord(String word) {
    Node parent = null;
    Node current = this.root;
    for (int i = 0; i < word.length(); ++i) {
      char curChar = word.charAt(i);

      if (!this.charFilter.isAccept(curChar)) {
        // false == isNotStopChar
        continue;
      }
      Node child = current.get(curChar);
      if (child == null) {
        child = new Node();
        current.put(curChar, child);
      }
      // deal for addOrder:(a,ab)
      if (current.isLeaf()) {
        current.setState(NodeState.LIKE_LEAF);
      }
      parent = current;
      current = child;
    }

    if (parent != null) {
      // deal for addOrder: (ab,a)
      if (!current.isEmpty()) {
        current.setState(NodeState.LIKE_LEAF);
      } else {
        current.setState(NodeState.LEAF);
      }
    }

    return this;
  }

  public DfaTree delWords(Collection<String> sensitiveWords) {
    for (String word : sensitiveWords) {
      this.delWord(word);
    }
    return this;
  }

  public DfaTree delWords(String... sensitiveWords) {
    for (String word : sensitiveWords) {
      this.delWord(word);
    }
    return this;
  }

  public DfaTree delWord(String word) {
    if (word == null || word.isEmpty()) {
      return this;
    }

    // find word
    Node current = this.root;
    List<Node> path = new ArrayList<>();

    for (int i = 0; i < word.length(); ++i) {
      char c = word.charAt(i);
      // isNotStopChar
      if (!charFilter.isAccept(c)) {
        continue;
      }

      current = current.get(c);
      if (current == null) {
        // not exists this word
        return this;
      }
      path.add(current);

    }

    // have this word, check NodeStatus
    if (!current.isLeaf() && !current.isLikeLeaf()) {
      // this word is not ban;
      return this;
    }

    // 更新父,子节点的状态
  /*
    current.setState(NodeState.NON_LEAF);
    for(int i = path.size()-1;i>=0;i--) {
      Node node = path.get(i);
      Node pre = i > 0 ? path.get(i - 1) : this.root;
      if(node.isEmpty()) {

      } else {
        // do nothing
      }
    }
  */

    int i = path.size() - 1;
    Node node = path.get(i);
    Node pre = i > 0 ? path.get(i - 1) : this.root;
    if (node.isEmpty()) {
      char key = word.charAt(i);
      // 1.
      pre.remove(key);
      // 2-1. 找到该链路向上最近的一个LikeLeaf变更为Leaf
      if (pre.isEmpty()) {
        if (pre.isLikeLeaf()) {
          pre.setState(NodeState.LEAF);
        } else if (i - 1 > 0) { // pre !=root

          for (int j = i - 2; j >= 0; j--) {
            Node node1 = path.get(j);
            if (node1.size() < 2 && node1.isLikeLeaf()) {
              node1.setState(NodeState.LEAF);
            } else if (node1.size() < 2 && !node1.isLikeLeaf()) {
              continue; // 继续向上查找
            } else { // node1.size() >=2
              break; // do nothing
            }
          }

        }
      } // else { // 2-2. 保持 do nothing

    } else {
      current.setState(NodeState.NON_LEAF);
    }


    return this;
  }

  public List<FoundResult> findWords(String text) {
    if (text == null) {
      return null;
    }
    List<FoundResult> foundResults = new ArrayList<>();
    Node pre, current;
    int length = text.length();
    FoundResult result = new FoundResult();

    // i: word-start, j: find-word-end
    for (int i = 0; i < length; ++i) {
      pre = this.root;
      result.setOff(i);
      result.setLen(0);

      FoundResult temp = null;
      for (int j = i; j < length; ++j) {
        char curChar = text.charAt(j);
        // is stop char
        if (!this.charFilter.isAccept(curChar)) {
          if (result.getLen() > 0) {
            result.setLen(result.getLen() + 1);
          } else {
            // incr word-start
            ++i;
            result.setOff(i);
          }
          continue; // continue for j
        }
        // checking match: [1],[2]

        current = pre.get(curChar);
        // [1]not match
        if (current == null) {
          // 由于最长匹配，若temp != null 说明之前匹配过伪叶子节点，回退将之前的结果添加
          if (temp != null) {
            foundResults.add(temp);
            // 回溯i,跳过已匹配部分, old_j ==  temp.off + temp.len-1
            i = temp.getOff() + temp.getLen() - 1;
          }
          break;  // break for j
        }

        // [2]match
        result.setLen(result.getLen() + 1);
        if (current.isNonLeaf()) {
          pre = current;
          continue; // continue for j
        }

        if (current.isLeaf()) {
          foundResults.add(result.copy());
          // 跳过已匹配部分
          i = j;
          break; // break for j
        } else {
          // 最长匹配
          pre = current;
          temp = result.copy();
          //continue; // continue for j
        }

      } // end for j
    } // end for i


    return foundResults;
  }

  public List<String> matchWords(String text) {
    if (text == null) {
      return null;
    }
    List<String> wordResults = new ArrayList<>();
    Node pre, current;
    int length = text.length();
    StringBuilder wordBuffer = new StringBuilder();

    // i: word-start, j: find-word-end
    for (int i = 0; i < length; ++i) {
      pre = this.root;
      wordBuffer.setLength(0);

      StringBuilder temp = null;
      for (int j = i; j < length; ++j) {
        char curChar = text.charAt(j);
        if (!this.charFilter.isAccept(curChar)) {
          if (wordBuffer.length() > 0) {
            wordBuffer.append(curChar);
          } else {
            // incr word-start
            ++i;
            //wordBuffer.setLength(0);
          }
          continue;
        }
        // checking match:

        current = pre.get(curChar);
        // not match
        if (current == null) {
          // 由于最长匹配，若temp != null 说明之前匹配过伪叶子节点，回退将之前的结果添加
          if (temp != null) {
            wordResults.add(temp.toString());
            // 跳过已匹配部分, old_j == i+temp.len or temp.off + temp.len
            i = i + temp.length();
          }
          break;
        }

        // match
        wordBuffer.append(curChar);
        if (current.isNonLeaf()) {
          pre = current;
          continue;
        }

        if (current.isLeaf()) {
          wordResults.add(wordBuffer.toString());
          // 跳过已匹配部分
          i = j;
          break;
        } else {
          // 最长匹配
          pre = current;
          temp = wordBuffer;
          //continue;
        }

      } // end for j
    } // end for i

    return wordResults;

  }


} // class DfaTree
